using Rogan.Utility.Common;
using Rogan.Utility.Extensions;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace Rogan.Data.Linq
{
	public class ExpressionTranslater<T> : ExpressionVisitor where T : Entity
	{
		private Dictionary<Type, Entity> tables = new Dictionary<Type, Entity>();
		private Expression root;
		private ReadOnlyCollection<ParameterExpression> currentContext;
		private NewExpression currentSelector;
		private MemberExpression currentSelectMember;
		public EntityQueryExpression EntityQueryExpression
		{
			get
			{
				return EntityQuery.Expression;
			}
		}
		public EntityQueryable<T> EntityQuery
		{
			get;
			private set;
		}
		public string CallMethodName
		{
			get;
			private set;
		}
		private Entity getTableByType(Type type)
		{
			if (tables.ContainsKey(type))
			{
				return tables[type];
			}
			Entity entity = DataHelper.Create(type)() as Entity;
			tables[type] = entity;
			return entity;
		}
		private bool IsQuery(Expression expression)
		{
			Type elementType = TypeHelper.GetElementType(expression.Type);
			return elementType != null && typeof(IQueryable<>).MakeGenericType(new[]
			{
				elementType
			}).IsAssignableFrom(expression.Type);
		}
		private static LambdaExpression GetLambda(Expression e)
		{
			while (e.NodeType == ExpressionType.Quote)
			{
				e = ((UnaryExpression)e).Operand;
			}
			if (e.NodeType == ExpressionType.Constant)
			{
				return ((ConstantExpression)e).Value as LambdaExpression;
			}
			return e as LambdaExpression;
		}
		private QueryOperator GetQueryOperatorByExpressType(ExpressionType NodeType)
		{
			switch (NodeType)
			{
			case ExpressionType.Add:
				return QueryOperator.Add;
			case ExpressionType.And:
				return QueryOperator.BitwiseAND;
			case ExpressionType.AndAlso:
				return QueryOperator.And;
			case ExpressionType.ArrayLength:
			case ExpressionType.Convert:
			case ExpressionType.ConvertChecked:
			case ExpressionType.UnaryPlus:
			case ExpressionType.Quote:
			case ExpressionType.TypeAs:
				throw new NotImplementedException();
			case ExpressionType.Divide:
				return QueryOperator.Divide;
			case ExpressionType.Equal:
				return QueryOperator.Equal;
			case ExpressionType.GreaterThan:
				return QueryOperator.Greater;
			case ExpressionType.GreaterThanOrEqual:
				return QueryOperator.GreaterOrEqual;
			case ExpressionType.LessThan:
				return QueryOperator.Less;
			case ExpressionType.LessThanOrEqual:
				return QueryOperator.LessOrEqual;
			case ExpressionType.Modulo:
				return QueryOperator.Modulo;
			case ExpressionType.Multiply:
			case ExpressionType.MultiplyChecked:
				return QueryOperator.Multiply;
			case ExpressionType.Negate:
			case ExpressionType.NegateChecked:
			case ExpressionType.Not:
				return QueryOperator.BitwiseNOT;
			case ExpressionType.NotEqual:
				return QueryOperator.NotEqual;
			case ExpressionType.Or:
				return QueryOperator.BitwiseOR;
			case ExpressionType.OrElse:
				return QueryOperator.Or;
			case ExpressionType.Subtract:
			case ExpressionType.SubtractChecked:
				return QueryOperator.Subtract;
			}
			throw new NotImplementedException();
		}
		public ExpressionTranslater(Expression root, EntityQueryable<T> entityQuery)
		{
			this.root = root;
			EntityQuery = entityQuery;
		}
		public void Translat()
		{
			Visit(root);
		}
		public object Execute()
		{
			string callMethodName;
			switch (callMethodName = CallMethodName)
			{
			case "First":
			case "FirstOrDefault":
			case "Single":
			case "SingleOrDefault":
			case "Last":
			case "LastOrDefault":
			{
				T t = EntityQuery.ToFirst();
				if (currentSelectMember != null)
				{
					return DataHelper.ConvertValue(currentSelectMember.Type, t);
				}
				if (currentSelector != null)
				{
					DataHelper.Create(currentSelector.Type);
					currentSelector.Type.GetProperties();
					return null;
				}
				return t;
			}
			case "Any":
				return EntityQuery.Exist();
			case "Count":
				return EntityQuery.Count();
			}
			List<T> list = EntityQuery.ToList();
			List<object> list2 = new List<object>();
			if (currentSelectMember != null)
			{
				foreach (Entity entity in list)
				{
					object value = entity.GetValue(currentSelectMember.Member.Name);
					list2.Add(DataHelper.ConvertValue(currentSelectMember.Type, value));
				}
				return DataHelper.ListDataToEnumerable<object>(list2, currentSelectMember.Type);
			}
			if (currentSelector != null)
			{
				Activator.CreateInstance(currentSelector.Type);
				currentSelector.Type.GetProperties();
				return null;
			}
			return list;
		}

	    public override Expression Visit(Expression exp)
		{
			return base.Visit(exp);
		}
		protected override Expression VisitMethodCall(MethodCallExpression m)
		{
			if (m.Method.DeclaringType == typeof(Queryable) || m.Method.DeclaringType == typeof(Enumerable))
			{
				if (root == m)
				{
					CallMethodName = m.Method.Name;
				}
				string name;
				switch (name = m.Method.Name)
				{
				case "Where":
					TranslatWhere(m.Type, m.Arguments[0], GetLambda(m.Arguments[1]));
					break;
				case "Select":
					TranslatSelect(m.Type, m.Arguments[0], GetLambda(m.Arguments[1]));
					break;
				case "OrderBy":
				case "OrderByDescending":
				case "ThenBy":
				case "ThenByDescending":
					TranslatOrderBy(m.Type, m.Arguments[0], GetLambda(m.Arguments[1]), m.Method.Name == "OrderByDescending" || m.Method.Name == "ThenByDescending");
					break;
				case "GroupBy":
					TranslatGroupBy(m.Type, m.Arguments[0], GetLambda(m.Arguments[1]));
					break;
				case "Distinct":
					TranslatDistinct(m.Type, m.Arguments[0]);
					break;
				case "Skip":
					TranslatSkip(m.Type, m.Arguments[0], m.Arguments[1]);
					break;
				case "Take":
					TranslatTake(m.Type, m.Arguments[0], m.Arguments[1]);
					break;
				case "First":
				case "FirstOrDefault":
				case "Single":
				case "SingleOrDefault":
				case "Last":
				case "LastOrDefault":
				case "Any":
				case "All":
				case "Count":
					if (m.Arguments.Count == 2)
					{
						TranslatWhere(m.Type, m.Arguments[0], GetLambda(m.Arguments[1]));
					}
					else
					{
						TranslatSource(m.Arguments[0]);
					}
					break;
				}
			}
			return null;
		}
		protected override Expression VisitConstant(ConstantExpression c)
		{
			if (IsQuery(c))
			{
				IQueryable arg_14_0 = (IQueryable)c.Value;
			}
			return c;
		}
		private void TranslatSource(Expression source)
		{
			base.Visit(source);
		}
		private void TranslatOrderBy(Type resultType, Expression source, LambdaExpression predicate, bool desc)
		{
			TranslatSource(source);
			currentContext = predicate.Parameters;
			if (predicate.Body.NodeType == ExpressionType.MemberAccess)
			{
				MemberExpression memberExpression = (MemberExpression)predicate.Body;
				Entity tableByType = getTableByType(memberExpression.Expression.Type);
				Field field = new Field(memberExpression.Member.Name, tableByType.GetName());
				EntityQueryExpression.AddOrderBy(new OrderByExpression(field, desc ? OrderByDirection.Asc : OrderByDirection.Desc));
			}
		}
		private void TranslatGroupBy(Type resultType, Expression source, LambdaExpression predicate)
		{
			TranslatSource(source);
			currentContext = predicate.Parameters;
			if (predicate.Body.NodeType == ExpressionType.MemberAccess)
			{
				MemberExpression memberExpression = (MemberExpression)predicate.Body;
				Entity tableByType = getTableByType(memberExpression.Expression.Type);
				if (EntityQueryExpression.GroupBy == null)
				{
					EntityQueryExpression.GroupBy = new GroupByExpression();
				}
				EntityQueryExpression.GroupBy.Fields.Add(new Field(memberExpression.Member.Name, tableByType.GetName()));
			}
		}
		private void TranslatDistinct(Type resultType, Expression source)
		{
			TranslatSource(source);
			EntityQuery.Distinct();
		}
		private void TranslatTake(Type resultType, Expression source, Expression exp)
		{
			TranslatSource(source);
			object valueByExpression = GetValueByExpression(exp);
			if (valueByExpression != null)
			{
				EntityQuery.Top((int)valueByExpression);
			}
		}
		private void TranslatSkip(Type resultType, Expression source, Expression exp)
		{
			TranslatSource(source);
			object valueByExpression = GetValueByExpression(exp);
			if (valueByExpression != null)
			{
				EntityQuery.Skip((int)valueByExpression);
			}
		}
		private void TranslatWhere(Type resultType, Expression source, LambdaExpression predicate)
		{
			TranslatSource(source);
			currentContext = predicate.Parameters;
			EntityQueryExpression.AddWhere(GetWhereExpression(predicate.Body));
		}
		private WhereExpression GetWhereExpression(Expression exp)
		{
			ExpressionType nodeType = exp.NodeType;
			if (nodeType <= ExpressionType.Call)
			{
				if (nodeType != ExpressionType.AndAlso)
				{
					if (nodeType != ExpressionType.Call)
					{
						goto IL_178;
					}
					MethodCallExpression methodCallExpression = (MethodCallExpression)exp;
					if (methodCallExpression.Method.Name == "Contains")
					{
						IEnumerable values = GetValueByExpression(methodCallExpression.Arguments[0]) as IEnumerable;
						Field fieldByExpression = GetFieldByExpression(methodCallExpression.Arguments[1]);
						return fieldByExpression.SelectIn(values);
					}
					if (!(methodCallExpression.Method.Name == "StartsWith") && !(methodCallExpression.Method.Name == "EndsWith"))
					{
						return null;
					}
					object valueByExpression = GetValueByExpression(methodCallExpression.Arguments[0]);
					Field fieldByExpression2 = GetFieldByExpression(methodCallExpression.Object);
					if (methodCallExpression.Method.Name == "StartsWith")
					{
						return fieldByExpression2.StartsWith(valueByExpression as string);
					}
					return fieldByExpression2.EndsWith(valueByExpression as string);
				}
			}
			else
			{
				switch (nodeType)
				{
				case ExpressionType.Equal:
				case ExpressionType.GreaterThan:
				case ExpressionType.GreaterThanOrEqual:
				case ExpressionType.LessThan:
				case ExpressionType.LessThanOrEqual:
					break;
				case ExpressionType.ExclusiveOr:
				case ExpressionType.Invoke:
				case ExpressionType.Lambda:
				case ExpressionType.LeftShift:
					goto IL_178;
				default:
					switch (nodeType)
					{
					case ExpressionType.Not:
						return GetWhereExpression(((UnaryExpression)exp).Operand).Not;
					case ExpressionType.NotEqual:
					case ExpressionType.OrElse:
						break;
					case ExpressionType.Or:
						goto IL_178;
					default:
						goto IL_178;
					}
					break;
				}
			}
			BinaryExpression binaryExpression = (BinaryExpression)exp;
			return GetWhereExpression(binaryExpression.Left, binaryExpression.Right, exp.NodeType);
			IL_178:
			return null;
		}
		private WhereExpression GetWhereExpression(Expression left, Expression right, ExpressionType NodeType)
		{
			if (NodeType == ExpressionType.AndAlso || NodeType == ExpressionType.OrElse)
			{
				return WhereExpression.Create(GetWhereExpression(left), GetWhereExpression(right), GetQueryOperatorByExpressType(NodeType));
			}
			if (FieldConvertable(left) && FieldConvertable(right))
			{
				return WhereExpression.Create(GetFieldByExpression(left), GetFieldByExpression(right), GetQueryOperatorByExpressType(NodeType));
			}
			if (FieldConvertable(left) && ValueConvertable(right))
			{
				return WhereExpression.Create(GetFieldByExpression(left), GetValueByExpression(right), GetQueryOperatorByExpressType(NodeType));
			}
			if (FieldConvertable(left) && ValueConvertable(right))
			{
				return WhereExpression.Create(GetValueByExpression(right), GetFieldByExpression(right), GetQueryOperatorByExpressType(NodeType));
			}
			return null;
		}
		private bool FieldConvertable(Expression exp)
		{
			if (exp.NodeType == ExpressionType.MemberAccess)
			{
				MemberExpression memberExpression = (MemberExpression)exp;
				if (memberExpression.Expression == null)
				{
					return false;
				}
				if (memberExpression.Expression.Type == currentContext[0].Type)
				{
					return true;
				}
			}
			return false;
		}
		private bool ValueConvertable(Expression exp)
		{
			return exp.NodeType == ExpressionType.Constant || exp.NodeType == ExpressionType.MemberAccess || exp.NodeType == ExpressionType.New || (exp.NodeType == ExpressionType.Convert && ValueConvertable(((UnaryExpression)exp).Operand));
		}
		private Field GetFieldByExpression(Expression exp)
		{
			if (exp.NodeType != ExpressionType.MemberAccess)
			{
				return null;
			}
			MemberExpression memberExpression = (MemberExpression)exp;
			Entity tableByType = getTableByType(memberExpression.Expression.Type);
			return new Field(memberExpression.Member.Name, tableByType.GetName());
		}
		private object GetValueByExpression(Expression exp)
		{
			if (!ValueConvertable(exp))
			{
				return null;
			}
			if (exp.NodeType == ExpressionType.Constant)
			{
				ConstantExpression constantExpression = (ConstantExpression)exp;
				return constantExpression.Value;
			}
			if (exp.NodeType == ExpressionType.MemberAccess)
			{
				if (FieldConvertable(exp))
				{
					return null;
				}
				MemberExpression memberExpression = (MemberExpression)exp;
				PropertyInfo property;
				if (memberExpression.Expression != null && memberExpression.Expression.NodeType == ExpressionType.Constant)
				{
					ConstantExpression constantExpression2 = (ConstantExpression)memberExpression.Expression;
					FieldInfo field = constantExpression2.Type.GetField(memberExpression.Member.Name);
					if (field != null)
					{
						return field.GetValue(constantExpression2.Value);
					}
					property = constantExpression2.Type.GetProperty(memberExpression.Member.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (property != null)
					{
						return property.GetValue(constantExpression2.Value, null);
					}
				}
				property = memberExpression.Type.GetProperty(memberExpression.Member.Name);
				if (property != null)
				{
					return property.GetValue(null, null);
				}
				return null;
			}
			else
			{
				if (exp.NodeType == ExpressionType.New)
				{
					NewExpression newExpression = (NewExpression)exp;
					object[] array = new object[newExpression.Arguments.Count];
					for (int i = 0; i < array.Length; i++)
					{
						array[i] = GetValueByExpression(newExpression.Arguments[i]);
					}
					return newExpression.Constructor.Invoke(array);
				}
				if (exp.NodeType == ExpressionType.Convert)
				{
					return GetValueByExpression(((UnaryExpression)exp).Operand);
				}
				return null;
			}
		}
		private void TranslatSelect(Type resultType, Expression source, LambdaExpression predicate)
		{
			TranslatSource(source);
			currentContext = predicate.Parameters;
			if (predicate.Body.NodeType == ExpressionType.Parameter)
			{
				ParameterExpression parameterExpression = (ParameterExpression)predicate.Body;
				if (parameterExpression.Type.IsSubclassOf(typeof(Entity)))
				{
					Entity tableByType = getTableByType(parameterExpression.Type);
					EntityQueryExpression.Select.Fields.Add(new Field("*", tableByType.GetName()));
					return;
				}
			}
			else
			{
				if (predicate.Body.NodeType == ExpressionType.MemberAccess)
				{
					currentSelectMember = (MemberExpression)predicate.Body;
					EntityQueryExpression.Select.Fields = new List<Field>
					{
						GetFieldByExpression(predicate.Body)
					};
					return;
				}
				if (predicate.Body.NodeType == ExpressionType.New)
				{
					NewExpression newExpression = (NewExpression)predicate.Body;
					currentSelector = newExpression;
					ReadOnlyCollection<Expression> arguments = newExpression.Arguments;
					for (int i = 0; i < arguments.Count; i++)
					{
						Expression expression = arguments[i];
						if (expression.NodeType == ExpressionType.MemberAccess)
						{
							MemberExpression memberExpression = (MemberExpression)expression;
							Entity tableByType2 = getTableByType(memberExpression.Expression.Type);
							MemberInfo member = memberExpression.Member;
							EntityQueryExpression.Select.Fields.Add(new Field(member.Name, tableByType2.GetName()));
						}
						else
						{
							if (expression.NodeType == ExpressionType.Constant)
							{
								ConstantExpression constantExpression = (ConstantExpression)expression;
								EntityQueryExpression.Select.Fields.Add(new Field("N'{0}'".FormatWith(new object[]
								{
									constantExpression.Value.ToStr()
								})).As(newExpression.Members[i].Name));
							}
						}
					}
				}
			}
		}
	}
}
